package xmsg

import (
	"fmt"
	"io"

	"gitee.com/shuohe/xutils/xbuf"
	"gitee.com/shuohe/xutils/xcrypto/base58"
	"gitee.com/shuohe/xutils/xerror"
)

const (
	FieldType_String = iota
	FieldType_Int
	FieldType_Bytes
)

type FieldDef struct {
	// 从 0 开始！！！
	Id        int
	Name      string
	BitMask   uint32
	Type      int
	MinLength int
	MaxLength int
	Validator IFieldValidator
}

type FieldDefs [32]*FieldDef

func (this *FieldDefs) Register(id int, name string, fieldType int, minLen, maxLen int, validator IFieldValidator) *FieldDef {
	this[id] = &FieldDef{
		Id:        id,
		Name:      name,
		BitMask:   uint32(1) << uint(id),
		Type:      fieldType,
		MinLength: minLen,
		MaxLength: maxLen,
		Validator: validator,
	}

	return this[id]
}

//==============================================================================

/**
 * @implements:
 *		xbuf.IMarshalable
 *		xbuf.IUnmarshalable
 */
type Bitmap32Message struct {
	fdefs  *FieldDefs
	bitmap uint32
	fields [32]Variant
}

func (this *Bitmap32Message) Clear() *Bitmap32Message {
	this.bitmap = 0
	for i := 0; i < 32; i++ {
		this.fields[i].SetNil()
	}
	return this
}

func (this *Bitmap32Message) ClearField(def *FieldDef) {
	this.bitmap &^= def.BitMask
	this.fields[def.Id].SetNil()
}

func (this *Bitmap32Message) Exists(def *FieldDef) bool {
	return this.fields[def.Id].IsNil()
}

func (this *Bitmap32Message) GetString(def *FieldDef) string {
	if this.fields[def.Id].IsNil() {
		return ""
	}
	if def.Type != FieldType_String {
		panic("Not a string field.")
	}
	return this.fields[def.Id].AsString()
}

func (this *Bitmap32Message) GetInt(def *FieldDef) int {
	if this.fields[def.Id].IsNil() {
		return 0
	}
	if def.Type != FieldType_Int {
		panic("Not a integer field.")
	}
	return this.fields[def.Id].AsInt()
}

func (this *Bitmap32Message) GetBytes(def *FieldDef) []byte {
	if this.fields[def.Id].IsNil() {
		return []byte{}
	}
	if def.Type != FieldType_Bytes {
		panic("Not a bytes field.")
	}
	if this.fields[def.Id].Cap == 0 {
		panic("Field value is a marshalable, use AsMarshalable().")
	}
	return this.fields[def.Id].AsBytes()
}

func (this *Bitmap32Message) SetString(def *FieldDef, value string) {
	if def.Type != FieldType_String {
		panic("Not a string field.")
	}
	this.fields[def.Id].SetString(value)
}

func (this *Bitmap32Message) SetInt(def *FieldDef, value int) {
	if def.Type != FieldType_Int {
		panic("Not a integer field.")
	}
	this.fields[def.Id].SetInt(value)
}

func (this *Bitmap32Message) SetBytes(def *FieldDef, value []byte) {
	if def.Type != FieldType_Bytes {
		panic("Not a bytes field.")
	}
	this.fields[def.Id].SetBytes(value)
}

func (this *Bitmap32Message) SetMarshalable(def *FieldDef, value xbuf.IMarshalable) {
	if def.Type != FieldType_Bytes {
		panic("Not a bytes field.")
	}
	this.fields[def.Id].SetMarshalable(value)
}

/**
 * 在调用 UnmarshalFromXBuf() 之前，要提前把 Unmarshalable 字段设置好。
 */
func (this *Bitmap32Message) SetUnmarshalable(def *FieldDef, value xbuf.IUnmarshalable) {
	if def.Type != FieldType_Bytes {
		panic("Not a bytes field")
	}
	this.fields[def.Id].SetUnmarshalable(value)
}

/**
 * @implements xbuf.IMarshalable::MarshalToXBuf
 */
func (this *Bitmap32Message) MarshalToXBuf(xb *xbuf.Buffer) {
	// 记下当前写指针位置，并跳过四个字节，用于后续填写位图
	wi1 := xb.Wi
	xb.EnsureWrite(4)
	xb.Wi += 4

	// 循环一次，写入存在的字段，同时计算位图
	this.bitmap = 0
	for i, def := range this.fdefs {
		fv := this.fields[i]
		if def == nil || fv.IsNil() {
			continue
		}
		if def.Type == FieldType_Int {
			// fv 一定是 int
			xb.PutVInt(fv.AsInt())
		} else if def.Type == FieldType_Bytes {
			// fv 可能是 []byte 或 xbuf.Marshalable
			if fv.Type == VarType_Bytes {
				if def.MaxLength == def.MinLength {
					xb.PutBytes(fv.AsBytes())
				} else {
					xb.PutVBytes(fv.AsBytes())
				}
			} else {
				fv.AsMarshalable().MarshalToXBuf(xb)
			}
		} else {
			// fv 一定是 string
			if def.MaxLength == def.MinLength {
				xb.PutStr(fv.AsString())
			} else {
				xb.PutVStr(fv.AsString())
			}
		}
		this.bitmap |= def.BitMask
	}

	// 写指针跳回开始位置，写入四字节的位图字段，然后写指针跳回当前
	wi2 := xb.Wi
	xb.Wi = wi1
	xb.PutUint32(this.bitmap)
	xb.Wi = wi2
}

/**
 * @implements xbuf.IUnmarshalable::UnmarshalFromXBuf
 */
func (this *Bitmap32Message) UnmarshalFromXBuf(xb *xbuf.Buffer) error {
	bmp, err := xb.GetUint32()
	if err != nil {
		return xerror.Wrap(err, "Failed to read field bitmap.")
	}
	this.bitmap = bmp

	for i, def := range this.fdefs {
		if def == nil || this.bitmap&def.BitMask == 0 {
			continue
		}

		if def.Type == FieldType_Int {
			// int
			fv, err := xb.GetVInt()
			if err != nil {
				return xerror.Wrapf(err, "Failed to read field '%s'.", def.Name)
			}
			this.fields[i].SetInt(fv)
		} else if def.Type == FieldType_Bytes {
			// 只要不是 Unmarshalable，就置为 []byte
			if this.fields[i].IsNil() || this.fields[i].Type != VarType_Unmarshalable {
				if def.MinLength == def.MaxLength {
					// 定长
					fv, err := xb.GetBytes(def.MinLength)
					if err != nil {
						return xerror.Wrapf(err, "Failed to read field '%s'.", def.Name)
					}
					this.fields[i].SetBytes(fv)
				} else {
					// 变长
					fv, err := xb.GetVBytes()
					if err != nil {
						return xerror.Wrapf(err, "Failed to read field '%s'.", def.Name)
					}
					this.fields[i].SetBytes(fv)
				}
			} else {
				err := this.fields[i].AsUnmarshalable().UnmarshalFromXBuf(xb)
				if err != nil {
					return xerror.Wrapf(err, "Failed to read field '%s'.", def.Name)
				}
			}
		} else {
			// 字符串
			if def.MinLength == def.MaxLength {
				fv, err := xb.GetStr(def.MinLength)
				if err != nil {
					return xerror.Wrapf(err, "Failed to read field '%s'.", def.Name)
				}
				this.fields[i].SetString(fv)
			} else {
				// 变长字符串或字节串
				fv, err := xb.GetVStr()
				if err != nil {
					return xerror.Wrapf(err, "Failed to read field '%s'.", def.Name)
				}
				this.fields[i].SetString(fv)
			}
		}
	}

	return nil
}

func (this *Bitmap32Message) Validate() error {
	for i, def := range this.fdefs {
		fv := this.fields[i]
		if def == nil || fv.IsNil() {
			continue
		}

		if def.Type == FieldType_Bytes && (len(fv.AsBytes()) < def.MinLength || def.MaxLength < len(fv.AsBytes())) {
			return xerror.Newf("Length error on field '%s'.", def.Name)
		}
		if def.Type == FieldType_String && (len(fv.AsString()) < def.MinLength || def.MaxLength < len(fv.AsString())) {
			return xerror.Newf("Length error on field '%s'.", def.Name)
		}

		if def.Validator != nil && !def.Validator.Validate(&fv) {
			return xerror.Newf("Format error on field '%s'.", def.Name)
		}
	}

	return nil
}

func (this *Bitmap32Message) Dump(indent string, w io.Writer) {
	for i, def := range this.fdefs {
		fv := this.fields[i]
		if def == nil || fv.IsNil() {
			continue
		}

		fmt.Fprint(w, indent, def.Name, ": ")

		if def.Type == FieldType_String {
			fmt.Fprint(w, "'", fv.AsString(), "'\n")
		} else if def.Type == FieldType_Bytes {
			fmt.Fprint(w, "base58://", base58.Encode(fv.AsBytes()), "\n")
		} else {
			fmt.Fprint(w, fv.AsInt(), "\n")
		}
	}
}

func NewMessage(fdefs *FieldDefs) *Bitmap32Message {
	return &Bitmap32Message{
		fdefs: fdefs,
	}
}
